#ifndef __INTERCODE_H__
#define __INTERCODE_H__

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "Err.h"
#include <stdbool.h>


enum oprTypes{
    EOPR,
    VARIABLE,
    CONSTANT,
    TEMP
};

enum interCodeTypes{
    EIC,
    LABEL,
    GOTO,
    FUNCTION,
    RETURNIC,
    ARG,
    PARAM,
    READ,
    WRITE,
    ASSIGN,
    COND,
    DEC,
    CALL    
};

enum opType
{
    AS,     // x = y
    ADD,    // x = y + z
    SUB,    // x = y - z
    MUL,    // x = y * z
    DIV,    // x = y / z
    ADDR,   // x = &y
    LSTAR,  // *x = y
    RSTAR,  // x = *y
    LRSTAR, // *x = *y
    EQ,     // x <relop> y
    NE,
    LE,
    LT,
    GE,
    GT
};


typedef struct _operand {
    int type;
    union {
        int varID;
        int constant;
        int tmpID;
    };
    bool addr;
} operand;

typedef struct _interCode
{
    int type;
    union {
        int labelID; //define Label || GOTO 
        char funcName[32]; //define Func
        operand opr; //RETURN || ARG || PARAM || READ ||WRITE
        struct 
        { 
            operand dst;
            operand src1, src2;
            int opType; 
        } assign;
        struct
        {   
            operand opr1, opr2;
            int opType;
            int labelID;
        } cond;
        struct
        {
            int varID;
            int size;
        } dec;
        struct
        {
            operand dst;
            char funcName[32];
        } call;
    };
    struct _interCode *prev, *next;
} interCode;

int labelNum;
int varNum;
int tmpNum;

extern interCode *IChead;
extern interCode *ICtail;

extern operand nullOpr;
extern operand oneOpr;
extern operand zeroOpr;

int getLabelNum();
int getVarNum();
int getTmpNum();
operand newOpr(int type, int value);
interCode* newIC(int type);
interCode* newLabelIC(int labelID);
interCode* newGotoIC(int labelID);
interCode* newFunctionIC(const char* funcName);
interCode* newReturnIC(operand opr);
interCode* newArgIC(operand opr);
interCode* newParamIC(operand opr);
interCode* newReadIC(operand opr);
interCode* newWriteIC(operand opr);
interCode* newAssignIC(operand dst, operand src1, operand src2, int opType);
interCode* newCondIC(operand opr1, operand opr2, int opType, int labelID);
interCode* newDecIC(int varID, int size);
interCode* newCallIC(operand dst, const char* funcName);
void insertIC(interCode* target);
interCode* mergeIC(interCode* IC1, interCode* IC2);
void operandToString(char* buffer, operand opr);
void printSingleCode(interCode* code);
void interCodeToString(char* buffer, interCode* code);
void interCodeToFile(interCode* codes, FILE* fp);

void printIC(interCode* codes);

#endif